/*
 * Copyright (c) 2009, Russell Weir
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE PROJECT CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

package scripting;

import Type;

/**
* A Lua scripting environment.
* <ul>
* <li>The Lua version information is not available unless the table library is loaded
* <li> All numbers that are returned from Lua come as Floats, as there is no distinction between integer and floating point types in Lua.
* <li> Lua can return multiple values from methods, which can be enabled with the multiReturnMode property.
* </ul>
* @author Russell Weir
**/
class VmLua extends Vm
{
	/**
	* If set, function calls will always return an Array<Dynamic>
	* to handle Lua's multiple return value function ability. Defaults
	* to false (off).
	**/
	public var multiReturnMode(default, __setMultiReturnMode):Bool;

	/**
	* Create a new Lua VM.
	*
	* @param loadStdLibs If set to false, no standard libraries will be automatically loaded
	* @throws String if vm can not be created.
	**/
	public function new(loadStdLibs:Bool=true) {
		super(Vm.LUA);
		handle = vmlua_create(Converter.toNeko(loadStdLibs));

		if(handle == null)
			throw "Unable to create vm";

		populateVersion();
	}

	public override function ucall(functionName:String,args:Array<Dynamic>) : Dynamic
	{
		return Converter.toHaxe(
			vmlua_call_function(
				handle,
				#if neko untyped functionName.__s, #else #error #end
				args));
	}

	public override function exec(code:String,chunkName:String=null) : Bool
	{
		return Converter.toHaxe(
			vmlua_exec(handle,
				#if neko untyped code.__s #else #error #end,
				Converter.toNeko(chunkName))
		);
	}

	public override function execBytes(code:haxe.io.Bytes,chunkName:String=null) : Bool
	{
		return Converter.toHaxe(
			vmlua_exec(handle, code.getData(), Converter.toNeko(chunkName))
		);
	}


	public override function execFile(filename:String) : Dynamic
	{
		return Converter.toHaxe(
			vmlua_exec_file(handle, Converter.toNeko(filename))
		);
	}

	public override function getError() : String
	{
		return Converter.toHaxe(vmlua_get_error(handle));
	}

	public override function loadStdLibs(libs:Array<String>) {
		for(l in libs) {
			if(!vmlua_openstdlib(handle,Converter.toNeko(l)))
				throw "Error opening " + l;
		}
		populateVersion();
	}

	public override function setPaths(paths:Array<String>) : Void
	{
		//"./?.lua;/usr/local/lib/lua/?.lua" etc
		var path : String = "";
		for(i in 0...paths.length) {
			if(paths[i] == null || paths[i] == "")
				continue;
			if(i > 0 && path.length > 0) path += ";";
			path += paths[i];
			if(path.charAt(path.length - 1) != "/")
				path += "/";
			path += "?.lua";
		}
		vmlua_set_path(handle, Converter.toNeko(path));
	}

	////////////////////////////////////////////////////////////////////////
	////                    Private methods                             ////
	////////////////////////////////////////////////////////////////////////
	private function populateVersion() : Void {
		version = Converter.toHaxe(vmlua_getversion(handle));
		versionRevision = 0;
		try {
			var numParts = version.split(" ").pop().split(".");
			versionMajor = Std.parseInt(numParts[0]);
			versionMinor = Std.parseInt(numParts[1]);
		} catch(e:Dynamic) {}
	}

	private function __setMultiReturnMode(v:Bool) {
		vmlua_setmultireturnmode(handle,v);
		return multiReturnMode = v;
	}

	private override function makeVmDate(d:Dynamic) : Dynamic {
		if(Std.is(d, Date)) {
			d = DateObject.ofDate(cast d);
		}
		if(Std.is(d, DateObject)) {
			return cast(d,DateObject).toObject();
		}
		throw "Invalid date object";
	}
	////////////////////////////////////////////////////////////////////////
	////                     Static methods                             ////
	////////////////////////////////////////////////////////////////////////
	static function __init__() {
		vmlua_initialize();
	}

	static var vmlua_initialize = neko.Lib.load("hxvmlua", "vmlua_initialize", 0);
	static var vmlua_call_function = neko.Lib.load("hxvmlua", "vmlua_call_function", 3);
	static var vmlua_create = neko.Lib.load("hxvmlua", "vmlua_create", 1);
	static var vmlua_exec = neko.Lib.load("hxvmlua", "vmlua_exec", 3);
	static var vmlua_exec_file = neko.Lib.load("hxvmlua", "vmlua_exec_file", 2);
	static var vmlua_get_error = neko.Lib.load("hxvmlua", "vmlua_get_error", 1);
	static var vmlua_getversion = neko.Lib.load("hxvmlua", "vmlua_getversion", 1);
	static var vmlua_load_bytes = neko.Lib.load("hxvmlua", "vmlua_load_bytes", 3);
	static var vmlua_load_file = neko.Lib.load("hxvmlua", "vmlua_load_file", 2);
	static var vmlua_makenativedate = neko.Lib.load("hxvmlua", "vmlua_makenativedate", 2);
	static var vmlua_openstdlib = neko.Lib.load("hxvmlua", "vmlua_openstdlib", 2);
	static var vmlua_setmultireturnmode = neko.Lib.load("hxvmlua","vmlua_setmultireturnmode",2);
	static var vmlua_set_path = neko.Lib.load("hxvmlua", "vmlua_set_path", 2);
}
